
#include "vty_input.hpp"
#include "telnet_opt.hpp"

#define CONTROL(X)  ((X) - '@') 

vty_input::vty_input(vty_ptr vty):
    _vty(vty),
    _telnet_opt(new telnet_opt()),
    _cp(0)
{
    //start();    /* FIXME */
};

void vty_input::start()
{
    /*
    _vty->output(_telnet_opt->get_option_str(telnet_opt::ECHO_OPT));
    _vty->output(_telnet_opt->get_option_str(telnet_opt::SGA_OPT));
    _vty->output(_telnet_opt->get_option_str(telnet_opt::LINE_OPT));
    _vty->output(_telnet_opt->get_option_str(telnet_opt::NAWS_OPT));
    */

    this->prompt();
}

void vty_input::stop() 
{
    _vty.reset();
    PDBG("stop vty_input\n");
};

void vty_input::prompt()
{
    if (!_vty->get_func_flag())
        _vty->output("%s", _vty->prompt().c_str());
}

void vty_input::redraw_line(const std::string& newline) 
{
    _buf = newline;
    _cp = _buf.length();
    _vty->output("%s", newline.c_str());
};

void vty_input::redraw_line()
{
    _vty->output("%s", _buf.c_str());
}

bool vty_input::auth()  /* FIXME */
{
    PDBG("PWD: %s\n", _buf.c_str());
    std::string tmp(_buf.begin(), _buf.end());
    if(_buf.empty()) return false;
    if (_vty->check_passwd(tmp) != true) 
        return false;
    _cp = 0;
    if (_vty->current_mode().compare("AUTH") == 0) _vty->change_mode("VIEW");
    else if (_vty->current_mode().compare("AUTH_ENABLE") == 0) _vty->change_mode("ENABLE");
    return true;
}

bool    
vty_input::exec_command()
{
    if (_vty->is_auth_mode()) { /* FIXME */
        auth();
        _buf.clear();
    }

    if (_vty->is_dialog()) {
        _vty->excute_command(_buf);
        _buf.clear();
        _cp = 0;
    } else if (!_buf.empty()) {
        string_ptr ptr(new std::string(_buf.begin(), _buf.end()));
        _history.push_front(ptr);
        _history.reset();
        _vty->excute_command(*ptr);
        _buf.clear();
        _cp = 0;
    }
    if (!_vty->is_dialog()) 
        prompt();
    return true;
}

bool    
vty_input::self_insert(char c)
{
    std::size_t diff_len;
    diff_len = _buf.length() - _cp;

    if (diff_len == 0) {
        _buf.push_back(c);
    } else {
        _buf.insert(_cp, 1, c);
    }
    _cp++;

    if (_vty->is_auth_mode())
        return true;

    for (int idx = (_cp-1); idx < (_buf.length()); idx++) {
        //PDBG("<<<< %c\n", _buf[idx]);
        _vty->output(_buf[idx]);
    }
    
    for (int idx=0; idx < diff_len; idx++) {
        _vty->output(TELNET_BACKWARD_CHAR);
    }
    return true;
}

void vty_input::prev_line()
{
    string_ptr sptr;
    if (_history.empty()) return;
    if (_history.prev(sptr) != 0) return;
    _vty->output(VTY_NEWLINE);
    prompt();
    redraw_line(*(sptr));
}

void vty_input::next_line()
{
    string_ptr sptr;
    if (_history.empty()) return;
    if (_history.next(sptr) != 0) return;
    _vty->output(VTY_NEWLINE);
    prompt();
    redraw_line(*(sptr));
}

void vty_input::forward_char()
{
    if ((_cp) < _buf.length()) {
        _vty->output(_buf[_cp]);
        _cp++;
        //PDBG("F: CP(%d)\n", _cp);
    }
}

void vty_input::backward_char()
{
    if (_buf.empty())  return;
    if (_cp == 0) return;
    _vty->output(TELNET_BACKWARD_CHAR);
    _cp--;
    //PDBG("F: CP(%d)\n", _cp);
}

bool vty_input::rm_backward_char() 
{
    if (_buf.empty()) 
        return true;

    _buf.erase((_buf.end() -1));
    _cp--;

    _vty->output(TELNET_BACKWARD_CHAR);
    _vty->output(TELNET_SAPCE_CHAR);
    _vty->output(TELNET_BACKWARD_CHAR);
    return true;
}

void vty_input::escape_map(unsigned char c)
{
    //PDBG(">>> %c\n", c);
    switch (c)
    {
        case ('A'):
            prev_line();
            break;
        case ('B'):
            next_line();
            break;
        case ('C'):
            forward_char();
            break;
        case ('D'):
            backward_char();
            break;
        default:
            break;
    }

    /* Go back to normal mode. */
    _escape = VTY_NORMAL;

};

int vty_input::handle_vty_more(unsigned char c)
{
    return 0;
}

int vty_input::handle_vty_escape(unsigned char c) 
{
    if (_escape == VTY_ESCAPE) {
        this->escape_map(c);
        return 1;
    }
    if (_escape != VTY_PRE_ESCAPE) {
        return 0;
    }
    switch(c) {
    case '[':
        _escape = VTY_ESCAPE;
        break;
    case 'b':
        _escape = VTY_NORMAL;
        break;
    case 'f':
        _escape = VTY_NORMAL;
        break;
    case 'd':
        _escape = VTY_NORMAL;
        break;
    case CONTROL('H'):
    case 0x7f:
        _escape = VTY_NORMAL;
        break;
    default:
        _escape = VTY_NORMAL;
        break;
    }
    return 1;
}

int
//vty_input::handle_input(unsigned char* input, std::size_t nbytes) 
vty_input::handle_input(std::string& input)
{
    std::size_t len = input.length();
    for (int i = 0; i < len; i++) {
        unsigned char item = (unsigned char)input[i];
        if (_vty->is_stop()) { stop(); return -1; } /* FIXME */
        
        if (_telnet_opt->check_opt(item)) {
            continue;
        }
        if (_telnet_opt->is_iac()) {
            //_telnet_opt->dump((unsigned char*)&(input[i]), input.size()-i);
            i += _telnet_opt->handle_opt(item);     /* FIXME */
            _telnet_opt->clear_iac();
            continue;
        }

        if (handle_vty_escape(item)) continue;

        switch (item) {
            case 0x08:
            case 0x7F:
                rm_backward_char();
                break;
            case '?':
                {
                std::string tmp(_buf.begin(), _buf.end());
                //PDBG("> %s", tmp.c_str());
                if (_vty->is_dialog()) self_insert(input[i]);
                else _vty->describe_command(tmp);
                }
                break;
            case '\t':
                {
                if (_vty->is_dialog()) self_insert(input[i]);
                else {
                    std::string tmp(_buf.begin(), _buf.end());
                    //PDBG("> %s", tmp.c_str());
                    _vty->complete_command(tmp);
                }
                }
                break;
            case '\r':
            case '\n':
                _vty->output("%s", VTY_NEWLINE);
                exec_command();
                break;
            case '\033':
                if (i + 1 < len&& input[i + 1] == '[') {
                    PDBG(">> VTY_ESCAPE !!\n");
                    _escape = VTY_ESCAPE;
                    i++;
                } else {
                    PDBG(">> VTY_PRE_ESCAPE !!\n");
                    _escape = VTY_PRE_ESCAPE;
                }
            default:
                if (item > 31 && item < 127) {
                    //PDBG(">>> %c <<\n", input[i]);
                    self_insert (input[i]);
                };
                break;
        }
    }

    return true;
}


